{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b399105c",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8058a456",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tkinter as tk\n",
    "from tkinter import ttk\n",
    "import random"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05a9d687",
   "metadata": {},
   "source": [
    "一个左右划分的布局"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "2f1ff5bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "def layout_example():\n",
    "    win = tk.Tk()\n",
    "    win.title(\"布局分割示例\")\n",
    "    win.geometry('600x400')\n",
    "    \n",
    "    \"\"\" \n",
    "        最外层PanedWindow布局：\n",
    "    \"\"\"\n",
    "    outer_paned = tk.PanedWindow(win, orient=tk.HORIZONTAL)\n",
    "    outer_paned.pack(fill=tk.BOTH, expand=True)\n",
    "    \n",
    "    \"\"\" \n",
    "        左侧Frame布局容器：\n",
    "            1. 顶部：固定高度，背景色为#7CCD7C\n",
    "            2. 底部：固定高度，背景色为#9AC0CD\n",
    "        关键尺寸配置参数，左侧Frame设置固定宽度150，并禁止自动收缩。如果不设定这个宽度大小，\n",
    "        就会导致右侧Frame占满左侧。\n",
    "    \"\"\"\n",
    "    left_frame = tk.Frame(outer_paned, bg='#7CCD7C', width=150)  # 固定宽度\n",
    "    left_frame.pack_propagate(False)  # 禁止根据子组件调整大小\n",
    "    outer_paned.add(left_frame)\n",
    "    \n",
    "    \"\"\" \n",
    "        左侧内部布局（顶部）\n",
    "    \"\"\"\n",
    "    left_top_frame = tk.Frame(left_frame, bg='#7CCD7C')\n",
    "    left_top_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)\n",
    "    tk.Label(left_top_frame, text=\"左侧顶部\", font=('Arial', 12)).pack()  # 添加内容\n",
    "    \n",
    "    \"\"\" \n",
    "        左侧内部布局（底部）\n",
    "    \"\"\"\n",
    "    left_bottom_frame = tk.Frame(left_frame, bg='#9AC0CD')\n",
    "    left_bottom_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)\n",
    "    tk.Label(left_bottom_frame, text=\"左侧底部\", font=('Arial', 12)).pack()  # 添加内容\n",
    "    \n",
    "    \"\"\" \n",
    "        右侧PanedWindow布局容器：\n",
    "            1. 顶部：固定高度，背景色为#7171C6\n",
    "            2. 底部：固定高度，背景色为#8968CD\n",
    "    \"\"\"\n",
    "    right_paned = tk.PanedWindow(outer_paned, orient=tk.VERTICAL, \n",
    "                                showhandle=True, sashrelief=tk.SUNKEN)\n",
    "    outer_paned.add(right_paned)\n",
    "    \n",
    "    \"\"\" \n",
    "        右侧内部布局（顶部）\n",
    "    \"\"\"\n",
    "    right_top_frame = tk.Frame(right_paned, bg='#7171C6')  # 深紫色\n",
    "    right_paned.add(right_top_frame)\n",
    "    tk.Label(right_top_frame, text=\"右侧顶部\", font=('Arial', 12)).pack()  # 添加内容\n",
    "    \n",
    "    \"\"\" \n",
    "        右侧内部布局（底部）\n",
    "    \"\"\"\n",
    "    right_bottom_frame = tk.Frame(right_paned, bg='#8968CD')  # 浅紫色\n",
    "    right_paned.add(right_bottom_frame)\n",
    "    tk.Label(right_bottom_frame, text=\"右侧底部\", font=('Arial', 12)).pack()  # 添加内容\n",
    "    \n",
    "    # 【关键修改】在窗口渲染后设置分割位置（确保尺寸计算完成）\n",
    "    win.update()  # 强制更新布局，计算组件实际尺寸\n",
    "    outer_paned.sash_place(0, 150, 0)  # 左右分割位置（左侧宽度150）\n",
    "    right_paned.sash_place(0, 0, 100)  # 右侧上下分割位置（上部高度100）\n",
    "    \n",
    "    win.mainloop()\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    layout_example()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca8a2c65",
   "metadata": {},
   "source": [
    "内容填充的应用Case"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2dcb9e34",
   "metadata": {},
   "outputs": [],
   "source": [
    "def dataprocess_frame():\n",
    "    win = tk.Tk()\n",
    "    win.title(\"数据预处理\")\n",
    "    win.geometry(\"800x600+200+200\")\n",
    "\n",
    "    \"\"\" 创建外层水平方向的 PanedWindow，实现左右分割 \"\"\"\n",
    "    outer_paned = tk.PanedWindow(win, orient=tk.HORIZONTAL)\n",
    "    outer_paned.pack(fill=tk.BOTH, expand=True)\n",
    "\n",
    "    \"\"\" 创建左侧的 Frame 用于放置预处理选项（关键优化区） \"\"\"\n",
    "    # 【优化1】固定左侧宽度250px，禁止自动收缩\n",
    "    left_frame = tk.Frame(outer_paned, padx=10, pady=10, width=250)\n",
    "    left_frame.pack_propagate(False)  # 禁止子组件影响父容器尺寸\n",
    "    outer_paned.add(left_frame)\n",
    "\n",
    "    # 数据归一化部分（添加内边距优化视觉）\n",
    "    data_normalize_frame = tk.Frame(left_frame, padx=5)\n",
    "    data_normalize_frame.pack(fill=tk.X, pady=5)\n",
    "    data_normalize_label = tk.Label(\n",
    "        data_normalize_frame, text=\"数据归一化\", font=(\"微软雅黑\", 12, \"bold\")\n",
    "    )\n",
    "    data_normalize_label.pack(anchor=tk.W, pady=3)  # 标签下留空\n",
    "    norm_choices = [\n",
    "        \"不归一化\",\n",
    "        \"Min-MAX归一化\",\n",
    "        \"标准化（Z-score）\",\n",
    "        \"Savitzky-Golay平滑滤波\",\n",
    "    ]\n",
    "    norm_var = tk.StringVar()\n",
    "    norm_var.set(norm_choices[1])\n",
    "    for choice in norm_choices:\n",
    "        radio = tk.Radiobutton(\n",
    "            data_normalize_frame, text=choice, variable=norm_var, value=choice\n",
    "        )\n",
    "        radio.pack(anchor=tk.W, pady=2)  # 单选按钮间距优化\n",
    "\n",
    "    # 添加水平分隔线（调整样式）\n",
    "    sep1 = ttk.Separator(left_frame, orient=tk.HORIZONTAL, style=\"TSeparator\")\n",
    "    sep1.pack(fill=tk.X, padx=5, pady=10)\n",
    "\n",
    "    # 特征提取部分（同上优化）\n",
    "    feature_extract_frame = tk.Frame(left_frame, padx=5)\n",
    "    feature_extract_frame.pack(fill=tk.X, pady=5)\n",
    "    feature_extract_label = tk.Label(\n",
    "        feature_extract_frame, text=\"特征提取\", font=(\"微软雅黑\", 12, \"bold\")\n",
    "    )\n",
    "    feature_extract_label.pack(anchor=tk.W, pady=3)\n",
    "    feat_choices = [\"原始信号\", \"FFT频谱\", \"包络谱\", \"小波变换\"]\n",
    "    feat_var = tk.StringVar()\n",
    "    feat_var.set(feat_choices[0])\n",
    "    for choice in feat_choices:\n",
    "        radio = tk.Radiobutton(\n",
    "            feature_extract_frame, text=choice, variable=feat_var, value=choice\n",
    "        )\n",
    "        radio.pack(anchor=tk.W, pady=2)\n",
    "\n",
    "    # 水平分隔线\n",
    "    sep2 = ttk.Separator(left_frame, orient=tk.HORIZONTAL)\n",
    "    sep2.pack(fill=tk.X, padx=5, pady=10)\n",
    "\n",
    "    # 训练集/测试集划分部分（按钮居中）\n",
    "    split_frame = tk.Frame(left_frame, padx=5)\n",
    "    split_frame.pack(fill=tk.X, pady=5)\n",
    "    split_label = tk.Label(\n",
    "        split_frame, text=\"训练集/测试集划分\", font=(\"微软雅黑\", 12, \"bold\")\n",
    "    )\n",
    "    split_label.pack(anchor=tk.W, pady=3)\n",
    "\n",
    "    split_options_frame = tk.Frame(split_frame)\n",
    "    split_options_frame.pack(fill=tk.X, pady=5)\n",
    "\n",
    "    split_scale = ttk.Scale(\n",
    "        split_options_frame, from_=0.01, to=0.99, orient=tk.HORIZONTAL, length=150\n",
    "    )\n",
    "    split_scale.set(0.2)\n",
    "    split_scale.pack(side=tk.LEFT, padx=5)\n",
    "\n",
    "    split_percent_var = tk.StringVar(value=\"20%\")\n",
    "    split_percent_label = tk.Label(split_options_frame, textvariable=split_percent_var)\n",
    "    split_percent_label.pack(side=tk.LEFT, padx=5)\n",
    "\n",
    "    def update_percentage_label(event):\n",
    "        value = split_scale.get()\n",
    "        split_percent_var.set(f\"{int(value*100)}%\")\n",
    "\n",
    "    split_scale.bind(\"<Motion>\", update_percentage_label)\n",
    "    split_scale.bind(\"<ButtonRelease-1>\", update_percentage_label)\n",
    "\n",
    "    # 【优化2】按钮居中显示\n",
    "    preprocess_button = tk.Button(split_frame, text=\"应用预处理\", width=20)\n",
    "    preprocess_button.pack(anchor=tk.CENTER, pady=5)  # 改为居中\n",
    "\n",
    "    \"\"\" 创建右侧垂直方向的 PanedWindow（关键优化区） \"\"\"\n",
    "    right_paned = tk.PanedWindow(\n",
    "        outer_paned, orient=tk.VERTICAL, showhandle=True, sashrelief=\"sunken\"\n",
    "    )\n",
    "    outer_paned.add(right_paned)\n",
    "\n",
    "    # 顶部控制区域（限制最小高度）\n",
    "    control_frame = tk.Frame(right_paned, padx=10, pady=10, height=120)  # 固定最小高度\n",
    "    control_frame.pack_propagate(False)  # 禁止内容拉伸高度\n",
    "    right_paned.add(control_frame)\n",
    "\n",
    "    update_button = tk.Button(\n",
    "        control_frame, text=\"更新可视化\", width=20, font=(\"宋体\", 12)\n",
    "    )\n",
    "    update_button.pack(pady=5)\n",
    "\n",
    "    # 可视化类型选择区域（优化组件间距）\n",
    "    visualize_type_frame = tk.Frame(control_frame)\n",
    "    visualize_type_frame.pack(fill=tk.X, pady=5)\n",
    "\n",
    "    visualize_type_label = tk.Label(\n",
    "        visualize_type_frame, text=\"可视化类型：\", font=(\"宋体\", 12)\n",
    "    )\n",
    "    visualize_type_label.pack(side=tk.LEFT, padx=5)\n",
    "\n",
    "    visualize_type_combobox = ttk.Combobox(\n",
    "        visualize_type_frame,\n",
    "        values=[\"原始信号\", \"FFT频谱\", \"包络谱\", \"时频图\", \"特征分布\"],\n",
    "        width=15,\n",
    "        state=\"readonly\",\n",
    "    )\n",
    "    visualize_type_combobox.set(\"原始信号\")\n",
    "    visualize_type_combobox.pack(side=tk.LEFT, padx=5)\n",
    "\n",
    "    fault_type_label = tk.Label(\n",
    "        visualize_type_frame, text=\"故障类型：\", font=(\"宋体\", 12)\n",
    "    )\n",
    "    fault_type_label.pack(side=tk.LEFT, padx=5)\n",
    "\n",
    "    fault_type_combobox = ttk.Combobox(\n",
    "        visualize_type_frame,\n",
    "        values=[\"正常\", \"内圈故障\", \"外圈故障\", \"滚动体故障\", \"保持架故障\"],\n",
    "        width=10,\n",
    "        state=\"readonly\",\n",
    "    )\n",
    "    fault_type_combobox.set(\"正常\")\n",
    "    fault_type_combobox.pack(side=tk.LEFT, padx=5)\n",
    "\n",
    "    # 底部可视化显示区域（自适应窗口）\n",
    "    display_frame = tk.Frame(right_paned, bg=\"#f0f0f0\")\n",
    "    right_paned.add(display_frame)\n",
    "\n",
    "    # 【优化3】Canvas自适应窗口大小（移除固定尺寸，使用fill/expand）\n",
    "    display_canvas = tk.Canvas(\n",
    "        display_frame, bg=\"white\"  # 移除固定width/height\n",
    "    )\n",
    "    display_canvas.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)\n",
    "\n",
    "    # 【优化4】动态绘制频谱图（根据当前Canvas尺寸调整）\n",
    "    def draw_spectrum():\n",
    "        display_canvas.delete(\"all\")\n",
    "        # 获取当前Canvas实际尺寸\n",
    "        canvas_width = display_canvas.winfo_width()\n",
    "        canvas_height = display_canvas.winfo_height()\n",
    "        \n",
    "        # 绘制坐标轴（使用实际尺寸计算边距）\n",
    "        margin = max(30, canvas_width // 20)  # 动态边距（至少30px）\n",
    "        display_canvas.create_line(\n",
    "            margin, canvas_height - margin, \n",
    "            canvas_width - margin, canvas_height - margin, \n",
    "            width=2\n",
    "        )  # x轴\n",
    "        display_canvas.create_line(\n",
    "            margin, margin, margin, canvas_height - margin, \n",
    "            width=2\n",
    "        )  # y轴\n",
    "\n",
    "        # 动态生成频谱数据（根据实际宽度调整点数）\n",
    "        points = []\n",
    "        for x in range(20, canvas_width - margin * 2, max(10, (canvas_width - margin*2)//50)):\n",
    "            y = canvas_height - margin - random.randint(50, canvas_height - margin*2 - 50)\n",
    "            points.append((x + margin, y))\n",
    "\n",
    "        # 绘制曲线（使用抗锯齿）\n",
    "        for i in range(len(points) - 1):\n",
    "            x1, y1 = points[i]\n",
    "            x2, y2 = points[i + 1]\n",
    "            display_canvas.create_line(x1, y1, x2, y2, fill=\"#3498db\", width=2, smooth=True)\n",
    "\n",
    "        # 动态调整文字位置（居中显示）\n",
    "        display_canvas.create_text(\n",
    "            canvas_width / 2, margin / 2, \n",
    "            text=\"信号频谱图\", font=(\"Arial\", 12, \"bold\")\n",
    "        )\n",
    "        display_canvas.create_text(\n",
    "            margin / 2, canvas_height / 2, \n",
    "            text=\"幅度\", font=(\"Arial\", 10), angle=90\n",
    "        )\n",
    "        display_canvas.create_text(\n",
    "            canvas_width / 2, canvas_height - margin / 2, \n",
    "            text=\"频率\", font=(\"Arial\", 10)\n",
    "        )\n",
    "\n",
    "    # 绑定窗口大小变化事件，自动重绘\n",
    "    win.bind(\"<Configure>\", lambda event: draw_spectrum())\n",
    "\n",
    "    # 初始绘制频谱图\n",
    "    draw_spectrum()\n",
    "\n",
    "    # 绑定更新按钮事件\n",
    "    def on_update_visualization():\n",
    "        draw_spectrum()\n",
    "\n",
    "    update_button.config(command=on_update_visualization)\n",
    "\n",
    "    # 【优化5】在窗口渲染完成后设置分割位置（确保尺寸准确）\n",
    "    win.update()  # 强制更新布局\n",
    "    outer_paned.sash_place(0, 250, 0)  # 左右分割位置（左侧固定250px）\n",
    "    right_paned.sash_place(0, 0, 120)  # 右侧上下分割位置（顶部固定120px）\n",
    "\n",
    "    win.mainloop()\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    dataprocess_frame()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
